package com.basker.pisces.domain.validation.validator;

import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

import org.apache.commons.lang3.ArrayUtils;

import com.basker.pisces.domain.command.option.CommandExecuteOption;
import com.basker.pisces.domain.command.option.SaveCommandExecuteOption;
import com.basker.pisces.domain.constant.DomainConstants.ObjectErrorCode;
import com.basker.pisces.domain.entity.iterate.EntityIterateEvent;
import com.basker.pisces.domain.entity.iterate.EntityIterator;
import com.basker.pisces.domain.entity.iterate.IEntityIterateHandler;
import com.basker.pisces.domain.entity.meta.AbstractEntity;
import com.basker.pisces.domain.entity.meta.Entity;
import com.basker.pisces.domain.property.meta.Property;
import com.basker.pisces.domain.validation.ValidationErrors;
import com.basker.pisces.domain.value.IValueObject;
import com.basker.pisces.service.exception.IObjectErrorsWrapper.ObjectError;

/**
 * 必填属性校验器
 *
 * @author hangwen
 * @see Property#getRequired()
 * @see IValueObject#isEmpty()
 */
public class RequiredPropertyValidator extends AbstractValidator {

    @Override
    public ValidationErrors validate(Object[] dataObjects) {
        Entity entity = this.getEntity();

        ValidationErrors errors = new ValidationErrors();

        EntityIterator iterator = new EntityIterator(entity, dataObjects, new IterateHandler(errors));
        iterator.iterate();

        return errors;
    }

    private class IterateHandler implements IEntityIterateHandler {
        private ValidationErrors errors;

        public IterateHandler(ValidationErrors errors) {
            this.errors = errors;
        }

        @Override
        public void handle(EntityIterateEvent event) {
            AbstractEntity entity = event.getCurrentEntity();
            Property<?>[] requiredProperties = this.getRrequiredProperties(entity);

            if (ArrayUtils.isEmpty(requiredProperties)) {
                return;
            }

            List<Object> dataObjects = event.getDataObjects();

            for (Object dataObject : dataObjects) {
                for (Property<?> property : requiredProperties) {
                    IValueObject valueObject = property.getValue(dataObject);
                    boolean valid = !IValueObject.isEmpty(valueObject);

                    if (!valid) {
                        errors.addError(createError(dataObject, property, valueObject));
                    }
                }
            }
        }

        private ObjectError createError(Object dataObject, Property<?> property, IValueObject valueObject) {
            String errorMessage = property.getRequiredViolationMessgae(valueObject);
            return new ObjectError(ObjectErrorCode.REQUIRED_VIOLATION, dataObject, null, errorMessage);
        }

        private Property<?>[] getRrequiredProperties(AbstractEntity entity) {
            Stream<Property<?>> stream = entity.getProperties().stream();

            CommandExecuteOption option = getContext().getOption();
            if (option instanceof SaveCommandExecuteOption) {
                Set<String> ignoredRequiredProperties = ((SaveCommandExecuteOption) option)
                        .getIgnoredRequiredProperties();

                if (!ignoredRequiredProperties.isEmpty()) {
                    stream = stream.filter(p -> !ignoredRequiredProperties.contains(p.getAlias()));
                }
            }

            return stream.filter(p -> p.getRequired()).toArray(s -> new Property[s]);
        }
    }
}
